﻿using System;
using System.Collections.Generic;
using Repid.Dapper;
using Repid.Entity;
using Repid.Core.DbSet;
using Repid.Utility.Helper;

namespace Repid.Core.Context
{
    /// <summary>
    /// 数据上下文
    /// </summary
    internal partial class DataContext : IDisposable
    {
        /// <summary>
        /// 私有Ef上下文
        /// </summary>
        private EntitiesContext efContext = null;

        /// <summary>
        /// 私有Dapper上下文
        /// </summary>
        private DapperContext dapperContext = null;

        /// <summary>
        /// 基础数据对象
        /// </summary>
        internal EntitiesContext Ef
        {
            get
            {
                if (efContext == null)
                {
                    efContext = new EntitiesContext("connString");
                }
                return efContext;
            }
        }

        /// <summary>
        /// 扩展数据对象
        /// </summary>
        internal DapperContext Dapper
        {
            get
            {
                if (dapperContext == null)
                {
                    dapperContext = new DapperContext(Ef.Database.Connection);
                }
                return dapperContext;
            }
        }

        /// <summary>
        /// 开始事务
        /// </summary>
        internal void BeginTransaction()
        {
            Dapper.BeginTransaction(Ef.Database.BeginTransaction().UnderlyingTransaction);
        }

        #region DbSet

        /// <summary>
        /// 仓储缓存
        /// </summary>
        private Dictionary<Type, object> dbSetCache = new Dictionary<Type, object>();

        /// <summary>
        /// 获取数据集合
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <typeparam name="R">集合类型</typeparam>
        /// <returns>数据集合</returns>
        private R DbSetByCache<T, R>()
            where T : EntityBase, new()
            where R : BaseDbSet<T>, new()
        {
            if (!dbSetCache.ContainsKey(typeof(T)))
            {
                var dbSet = new R();
                // 设置上下文
                dbSet.Init(this);
                // 加入缓存中
                dbSetCache.Add(typeof(T), dbSet);

                return dbSet;
            }
            else { return (R)dbSetCache[typeof(T)]; }
        }

        /// <summary>
        /// 获取数据集合
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <typeparam name="R">集合类型</typeparam>
        /// <returns>数据集合</returns>
        internal R DbSet<T, R>()
            where T : EntityBase, new()
            where R : BaseDbSet<T>, new()
        {
            string entityName = typeof(T).Name;
            return GetType().GetProperty(entityName).GetValue(this, null) as R;
        }

        #endregion

        /// <summary>
        /// 保存变更
        /// </summary>
        /// <returns>受影响行数</returns>
        internal int SaveChanges()
        {
            if (efContext != null)
            {
                try
                {
                    int result = efContext.SaveChanges();

                    if (dapperContext != null) { dapperContext.Commit(); }

                    return result;
                }
                catch (Exception ex)
                {
                    LogChangeTracker();

                    if (dapperContext != null) { dapperContext.Rollback(); }

                    throw ex;
                }
            }
            return 0;
        }

        internal void LogChangeTracker()
        {
            try
            {
                LogHelper.Info(JsonHelper.Serialize(efContext.ChangeTracker.Entries()));
            }
            catch { LogHelper.Info("LogChangeTracker Error"); }
        }

        #region 释放资源

        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            Dispose(true); GC.SuppressFinalize(this);
        }

        /// <summary>
        /// 释放资源
        /// </summary>
        /// <param name="disposing">是否释放</param>
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (efContext != null)
                {
                    try
                    {
                        efContext.Dispose();
                        efContext = null;
                    }
                    catch { }
                }
            }
        }

        #endregion
    }
}